package com.xu.cloud.common.auth;

import com.xu.cloud.common.exception.ErrorCodeEnum;
import com.xu.cloud.common.exception.XuCloudException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

/**
 * 鉴权aop拦截器
 *
 * @author xuguan
 * @since 2024/9/24
 */
@Configuration
public class AuthorizationCheckAdvisingBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {

    public static final Logger log = LoggerFactory.getLogger(AuthorizationCheckAdvisingBeanPostProcessor.class);

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        final Pointcut cpc = new AnnotationMatchingPointcut(Authorization.class, true);
        final Pointcut mpc = new AnnotationMatchingPointcut(null, Authorization.class, true);
        final ComposablePointcut composablePointcut = new ComposablePointcut(cpc).union(mpc);

        this.advisor = new DefaultPointcutAdvisor(composablePointcut, new AuthorizationCheckMethodInterceptor());
    }

    private static class AuthorizationCheckMethodInterceptor implements MethodInterceptor {

        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            final Authorization methodAuthorization = AnnotationUtils.findAnnotation(methodInvocation.getMethod(), Authorization.class);
            final Authorization typeAuthorization = AnnotationUtils.findAnnotation(methodInvocation.getThis().getClass(), Authorization.class);
            // 方法上注解优先级高于类上注解
            Authorization authorization = Objects.nonNull(methodAuthorization)
                    ? methodAuthorization
                    : typeAuthorization;

            if (Objects.isNull(authorization)) {
                return methodInvocation.proceed();
            }

            final String hasRole = authorization.hasRole();
            final String hasPermission = authorization.hasPermission();

            if (!StringUtils.hasText(hasRole) && !StringUtils.hasText(hasPermission)) {
                return methodInvocation.proceed();
            }

            // 鉴权
            final AuthContext authContext = AuthContextHolder.getAuthContext();
            if (Objects.isNull(authContext)) {
                throw new XuCloudException(ErrorCodeEnum.UNAUTHORIZED.getCode(), ErrorCodeEnum.UNAUTHORIZED.getMessage());
            }

            final List<String> roles = authContext.getRoles();
            final List<String> permissions = authContext.getPermissions();

            if (StringUtils.hasText(hasRole)) {
                if (CollectionUtils.isEmpty(roles) || !roles.contains(hasRole)) {
                    throw new XuCloudException(ErrorCodeEnum.UNAUTHORIZED.getCode(), ErrorCodeEnum.UNAUTHORIZED.getMessage());
                }
            }

            if (StringUtils.hasText(hasPermission)) {
                if (CollectionUtils.isEmpty(permissions) || !permissions.contains(hasPermission)) {
                    throw new XuCloudException(ErrorCodeEnum.UNAUTHORIZED.getCode(), ErrorCodeEnum.UNAUTHORIZED.getMessage());
                }
            }

            return methodInvocation.proceed();
        }
    }
}

